﻿// 游戏状态
const GameStatus = {
    END: 0,     // 已结束
    PAUSED: 1,  // 暂停中
    PLAYING: 2  // 游戏中
}

var gExistentBlocks = null  // 累积的方块

/**
 * 游戏类的构造方法。
 * @param gameBoard         游戏区域
 * @param gameBoardDrawer   游戏区域绘图者
 * @param preview           预览区域
 * @param previewDrawer     预览区域绘图者
*/
function Game(gameBoard, gameBoardDrawer, preview, previewDrawer) {
    var mCurBlock = null                                            // 当前方块
    var mDifficulty = 1                                             // 难度
    const mDifficultyUpEvent = document.createEvent("customEvent")  // 难度增加事件
    var mEnableMusic = false                                        // 音乐开关
    const mGameBoard = gameBoard                                    // 游戏区域
    const mGameBoardDrawer = gameBoardDrawer                        // 游戏区域绘图者
    const mGameOverEvent = document.createEvent("customEvent")      // 游戏结束事件
    const mMusicPlayer = document.getElementById("IDC_MUSIC")       // 音乐播放者
    var mNextBlock = null                                           // 下一个方块
    const mPreview = preview                                        // 预览区域
    const mPreviewDrawer = previewDrawer                            // 预览区域绘图者
    var mScore = 0                                                  // 得分
    const mScoreEvent = document.createEvent("customEvent")         // 得分事件
    var mStatus = GameStatus.END                                    // 状态
    var mTimer = null                                               // 定时器
    var mTop = Const.GAME_BOARD_GRID_COUNT_V + 1                    // 有方块的最高行

    // 初始化成员
    mDifficultyUpEvent.initEvent(Const.UM_DIFFICULTY_UP, false, false)
    mGameOverEvent.initEvent(Const.UM_GAME_OVER, false, false)
    mScoreEvent.initEvent(Const.UM_SCORE, false, false)

    // 画游戏区域
    drawGameBoard()

    // 画预览区域
    drawPreview()

    /**
     * 检查是否存在满行。
     */
    function checkRow() {
        // 更新"有方块的最高行"
        const top = mCurBlock.getTop(mCurBlock)
        if (top < mTop) {
            mTop = top
        }

        var released = 0   // 消除的行数
        const bottom = mCurBlock.getBottom(mCurBlock)

        // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
        for (var i = mTop; i <= bottom; i++) {
            for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                // 如果该行有空的，就跳过该行
                if (!gExistentBlocks[i][j].mUsed) {
                    break
                }
                // 如果该行满了，消除该行
                if (j == Const.GAME_BOARD_GRID_COUNT_H - 1) {
                    releaseRow(i)
                    released++
                }
            }
        }

        if (released > 0) {
            // 因为消行了，所以需要重绘
            drawGameBoard()
            drawExistentBlock()

            // 计算得分
            mScore += released * released * 100

            // 发送通知
            mScoreEvent.arg1 = mScore
            document.dispatchEvent(mScoreEvent)

            // 计算难度
            if (Math.floor(mScore / Const.DIFF_BASE) + 1 > mDifficulty && mDifficulty < Const.DIFF_MAX) {
                // 增加难度
                mDifficulty++
                window.clearInterval(mTimer)
                const elapse = Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1)
                mTimer = window.setInterval(onTimer, elapse)
                
                // 发送通知
                mDifficultyUpEvent.arg1 = mDifficulty
                document.dispatchEvent(mDifficultyUpEvent)
            }
        }
    }

    /**
     * 随机创建一种方块。
     * @return 方块
     */
    function createRandBlock() {
        const random = Math.floor(Math.random() * 10) % 7 + 1

        switch (random) {
            case 1:
                return new Block1()
            case 2:
                return new Block2()
            case 3:
                return new Block3()
            case 4:
                return new Block4()
            case 5:
                return new Block5()
            case 6:
                return new Block6()
            case 7:
                return new Block7()
        }

        return new Block1()
    }

    /**
     * 画累积的方块。
     */
    function drawExistentBlock() {
        for (var i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
            for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                if (gExistentBlocks[i][j].mUsed) {
                    mGameBoardDrawer.fillStyle = gExistentBlocks[i][j].mColor
                    mGameBoardDrawer.fillRect(Const.GRID_SIZE * j, Const.GRID_SIZE * i, Const.GRID_SIZE, Const.GRID_SIZE)
                    Block.drawEdge(mGameBoardDrawer, Const.GRID_SIZE * j, Const.GRID_SIZE * i)
                }
            }
        }
    }

    /**
     * 画游戏区域。
     */
    function drawGameBoard() {
        mGameBoardDrawer.fillStyle = Const.GAME_BOARD_COLOR
        mGameBoardDrawer.fillRect(0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT)
        mGameBoard.repaint()
    }

    /**
     * 画预览区域。
     */
    function drawPreview() {
        mPreviewDrawer.fillStyle = Const.PREVIEW_COLOR
        mPreviewDrawer.fillRect(0, 0, Const.PREVIEW_WIDTH, Const.PREVIEW_HEIGHT)
        if (mNextBlock != null) {
            mNextBlock.enterPreview(mNextBlock, mPreviewDrawer)
        }
        mPreview.repaint()
    }

    /**
     * 游戏结束。
     * @param isWon  胜利则为 true，否则为 false
     */
    function gameOver(isWon) {
        // 将状态设置为已结束
        mStatus = GameStatus.END

        // 停止定时器
        window.clearInterval(mTimer)

        // 停止播放音乐
        if (mEnableMusic) {
            mMusicPlayer.pause()
            mMusicPlayer.load()
        }

        // 发送通知
        mGameOverEvent.arg1 = isWon
        document.dispatchEvent(mGameOverEvent)
    }
    
    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    this.getStatus = function() {
        return mStatus
    }

    /**
     * 判断是否失败。
     * @return 失败则返回 true，否则返回 false
     */
    function isLost() {
        return (mTop == 0 && gExistentBlocks[0][4].mUsed)
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    function isWon() {
        return mScore >= Const.WIN_NUMBER
    }

    /**
     * 方块下落。
     */
    function moveBlockDown() {
        if (mStatus != GameStatus.PLAYING) {
            return
        }

        const canMoveDown = mCurBlock.moveDown(mCurBlock)
        if (!canMoveDown) {
            // 检查是否存在满行
            checkRow()

            // 判断是否胜利
            if (isWon()) {
                gameOver(true)
                return
            }

            // 判断是否失败
            if (isLost()) {
                gameOver(false)
                return
            }

            // 产生新的方块
            nextBlock()
        }
    }

    /**
     * 产生新的方块。
     */
    function nextBlock() {
        // 创建当前方块
        if (mCurBlock != null) {
            mCurBlock = mNextBlock
        } else {
            mCurBlock = createRandBlock()
        }

        // 创建下一个方块
        mNextBlock = createRandBlock()

        // 当前方块进入游戏区域
        mCurBlock.enterGameBoard(mCurBlock, mGameBoard, mGameBoardDrawer)

        // 下一个方块进入预览区域
        drawPreview()
    }

    /**
     * 按键事件的响应方法。
     * @param key 按键
     */
    this.onKeyPressed = function(key) {
        if (mStatus != GameStatus.PLAYING) {
            return
        }

        switch (key) {
            case Const.KEY_DOWN:
                moveBlockDown()
                break
            case Const.KEY_LEFT:
                mCurBlock.moveLeft(mCurBlock)
                break
            case Const.KEY_RIGHT:
                mCurBlock.moveRight(mCurBlock)
                break
            case Const.KEY_UP:
                mCurBlock.transform(mCurBlock)
                break
        }
    }

    /**
     * 定时器事件的响应方法。
     */
    function onTimer() {
        moveBlockDown()
    }

    /**
     * 游戏暂停。
     */
    this.pause = function() {
        if (mStatus == GameStatus.PLAYING) {
            // 停止定时器
            window.clearInterval(mTimer)

            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.pause()
            }

            // 将状态设置为已暂停
            mStatus = GameStatus.PAUSED
        } else if (mStatus == GameStatus.PAUSED) {
            // 开启定时器
            mTimer = window.setInterval(onTimer, Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1))

            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.play()
            }

            // 将状态设置为游戏中
            mStatus = GameStatus.PLAYING
        }
    }

    /**
     * 播放音乐。
     * @return 正在播放音乐则为 true，否则为 false
     */
    this.playMusic = function() {
        mEnableMusic = !mEnableMusic

        if (mEnableMusic) {
            if (mStatus == GameStatus.PLAYING) {
                mMusicPlayer.play()
            }
        } else {
            mMusicPlayer.pause()
        }

        return mEnableMusic
    }

    /**
     * 消行。
     * @param row   行号
     */
    function releaseRow(row) {
        // 最上行
        if (row == 0) {
            for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                gExistentBlocks[row][j].mUsed = false
            }
            mTop++
            return
        }

        // 非最上行
        for (var i = row; i > mTop; i--) {
            for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                gExistentBlocks[i][j].mUsed = gExistentBlocks[i - 1][j].mUsed
                gExistentBlocks[i][j].mColor = gExistentBlocks[i - 1][j].mColor
            }
        }

        for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
            gExistentBlocks[mTop][j].mUsed = false
        }

        mTop++
    }

    /**
     * 游戏开始。
     */
    this.start = function() {
        // 画游戏区域
        drawGameBoard()

        // 画预览区域
        drawPreview()

        // 创建方块
        gExistentBlocks = new Array(Const.GAMEBOARD_GRID_COUNT_V)
        for (var i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
            gExistentBlocks[i] = new Array(Const.GAME_BOARD_GRID_COUNT_H)
            for (var j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                gExistentBlocks[i][j] = new ColorGrid()
            }
        }
        mCurBlock = null
        mNextBlock = null
        nextBlock()

        // 重置难度
        mDifficulty = 1

        // 重置分数
        mScore = 0

        // 开启定时器
        if (mTimer != null) {
            window.clearInterval(mTimer)
        }
        mTimer = window.setInterval(onTimer, Const.TIMER_INIT)

        // 播放音乐
        if (mEnableMusic) {
            mMusicPlayer.play()
        }

        // 将状态设置为游戏中
        mStatus = GameStatus.PLAYING
    }
}
